package com.seadragon.app.ksr.loader;

import static com.seadragon.app.ksr.model.KsrConstants.gf;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.transaction.Transactional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;

import com.seadragon.app.ksr.model.SHPFileRecord;
import com.seadragon.app.ksr.model.shape.PolygonShape;
import com.seadragon.app.ksr.model.tiger.County;
import com.seadragon.app.ksr.model.tiger.State;
import com.seadragon.app.ksr.service.SearchService;
import com.seadragon.app.ksr.util.FileUtils;
import com.seadragon.app.ksr.util.IndexManager;
import com.seadragon.app.ksr.util.tiger.DBFFileReader;
import com.seadragon.app.ksr.util.tiger.SHPFileReader;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Polygon;


@Component("countyLoader")
public class CountyLoader {

	private static Logger logger = LoggerFactory.getLogger(CountyLoader.class);
	private static final String SHP_FILE_EXTENSION = ".shp";
	private static final String DBF_FILE_EXTENSION = ".dbf";
	private static final String WORK_FOLDER = "work";
	private DBFFileReader dbfFileReader;
	private SHPFileReader shpFileReader;
	@Autowired
	private IndexManager indexManager;
	@Autowired
	private SearchService searchService;
	
	private int from = 0;
	private String data = null;
	public static final ApplicationContext appCtx = new ClassPathXmlApplicationContext("ksr-application-context-sa-postgres.xml");

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		int from = 0;
		String data = null;
		for(int i = 0; i < args.length; i++){
			if(args[i].equalsIgnoreCase("-from")){
				from  = Integer.parseInt(args[++i]);
			}else if(args[i].equalsIgnoreCase("-data")){
				data = args[++i];
			}
		}
		CountyLoader loader = (CountyLoader) appCtx.getBean("countyLoader");
		if(from > 0)
			loader.setFrom(from);
		loader.setData(data);
		logger.debug("Start to load " + args[0]);
		byte[] buffer = new byte[1024];
		File file = new File(data);
		if (!file.exists()) {
			logger.error("File doesn't exist: " + args[0]);
			return;
		}
		if (!file.isDirectory()) {
			logger.error("This is not a directory: " + args[0]);
			return;
		}
		File[] files = file.listFiles();
		File workFolder = new File(WORK_FOLDER);
		if (workFolder.exists()) {
			for (File f : workFolder.listFiles()) {
				f.delete();
			}
		} else {
			workFolder.mkdir();
		}

		for (int i = 0; i < files.length; i++) {
			try {
				ZipInputStream zis = new ZipInputStream(new FileInputStream(files[i]));
				// get the zipped file list entry
				ZipEntry ze = zis.getNextEntry();
				// All the files have the same base name
				String fileName = null;
				while (ze != null) {
					fileName = ze.getName();
					File newFile = new File(workFolder + File.separator + fileName);

//					System.out.println("file unzip : " + newFile.getAbsoluteFile());

					// create all non exists folders
					// else you will hit FileNotFoundException for compressed
					// folder
					new File(newFile.getParent()).mkdirs();

					FileOutputStream fos = new FileOutputStream(newFile);

					int len;
					while ((len = zis.read(buffer)) > 0) {
						fos.write(buffer, 0, len);
					}
					fos.close();
					ze = zis.getNextEntry();
				}
				zis.closeEntry();
				zis.close();
				loader.load(workFolder, FileUtils.getBaseName(fileName));
			} catch (FileNotFoundException ex) {
				logger.error(ex.getMessage(), ex);
			} catch (IOException ex) {
				logger.error(ex.getMessage(), ex);
			}
		}
	}

	 public String getData() {
		return data;
	}

	public void setData(String data) {
		this.data = data;
	}

	public int getFrom() {
		return from;
	}

	public void setFrom(int from) {
		this.from = from;
	}

	@Transactional
	 public void load(File directory, String baseName) throws FileNotFoundException, IOException {
		dbfFileReader = new DBFFileReader(new File(directory + File.separator + baseName + DBF_FILE_EXTENSION));
		shpFileReader = new SHPFileReader(new File(directory + File.separator + baseName + SHP_FILE_EXTENSION));
		logger.info("Processing " + directory + File.separator + baseName + DBF_FILE_EXTENSION);
		int count = 0;
		while (dbfFileReader.hasNext() && shpFileReader.hasNext()) {
			List<Object> dbfRecord = dbfFileReader.next();
			SHPFileRecord shpRecord = shpFileReader.next();
			count++;
			if(count < from) continue;
			int index = 0;
			County county = new County();
			county.setStatefp((String) dbfRecord.get(index++));
			State state = searchService.searchStateById(county.getStatefp());
			if(state != null)
				county.setState(state.getStusps());
			
			county.setCountyfp((String) dbfRecord.get(index));
			index += 2;
			county.setGeoid((String)dbfRecord.get(index++));
			county.setName((String) dbfRecord.get(index++));
			index += 7;
			county.setFuncStat((String) dbfRecord.get(index));
			index += 3;
			double lat = Double.parseDouble((String) dbfRecord.get(index++));
			double lng = Double.parseDouble((String) dbfRecord.get(index++));
			Coordinate coor = new Coordinate(lng, lat);
			county.setIntpt(gf.createPoint(coor));
			Polygon[] polygons = new Polygon[shpRecord.getRecordContent().getNumOfParts()];
			PolygonShape ps = (PolygonShape)shpRecord.getRecordContent();

			for(int i = 0; i < polygons.length; i++){
				if(i == polygons.length - 1){
					polygons[i] = gf.createPolygon(Arrays.copyOfRange(ps.getPoints(), shpRecord.getRecordContent().getParts()[i], ps.getPoints().length));
				}else{
					polygons[i] = gf.createPolygon(Arrays.copyOfRange(ps.getPoints(), shpRecord.getRecordContent().getParts()[i], shpRecord.getRecordContent().getParts()[i+1]));
				}
			}
			county.setGeom(gf.createMultiPolygon(polygons));
			if(shpRecord.getRecordContent().getNumOfParts() > 1)
				logger.info(county.getName() + ", " + county.getState() + " has parts: " + shpRecord.getRecordContent().getNumOfParts());
			try{
				indexManager.insertDocument(county);
			}catch(Exception ex){
				logger.error(ex.getMessage(), ex);
			}
			logger.info(count + ": " + county.getName());
		}
		dbfFileReader.close();
		shpFileReader.close();
	}
}
